package org.syntax.jedit.tokenmarker;

/*
 * XMLTokenMarker.java - XML token marker
 * Copyright (C) 1998, 1999 Slava Pestov
 * Copyright (C) 2001 Tom Bradford
 *
 * You may use and modify this package for any purpose. Redistribution is
 * permitted, in both source and binary form, provided that this notice
 * remains intact in all source distributions of this package.
 */

import javax.swing.text.Segment;

import org.syntax.jedit.SyntaxUtilities;

/**
 * XML Token Marker Rewrite
 *
 * @author Tom Bradford
 * @version $Id: XMLTokenMarker.java,v 1.5 2001/07/29 20:45:43 tom Exp $
 */
public class XMLTokenMarker extends TokenMarker {
   public XMLTokenMarker() {
   }
   
   public byte markTokensImpl(byte token, Segment line, int lineIndex) {
      char[] array = line.array;
      int offset = line.offset;
      int lastOffset = offset;
      int length = line.count + offset;
      
      // Ugly hack to handle multi-line tags
      boolean sk1 = token == Token.KEYWORD1;
      
      for ( int i = offset; i < length; i++ ) {
         int ip1 = i+1;
         char c = array[i];
         switch ( token ) {
            case Token.NULL: // text
               switch ( c ) {
                  case '<':
                     addToken(i-lastOffset, token);
                     lastOffset = i;
                     if ( SyntaxUtilities.regionMatches(false, line, ip1, "!--") ) {
                        i += 3;
                        token = Token.COMMENT1;
                     }
                     else if ( array[ip1] == '!' ) {
                        i += 1;
                        token = Token.COMMENT2;
                     }
                     else if ( array[ip1] == '?' ) {
                        i += 1;
                        token = Token.KEYWORD3;
                     }
                     else
                        token = Token.KEYWORD1;
                     break;
                     
                  case '&':
                     addToken(i - lastOffset, token);
                     lastOffset = i;
                     token = Token.LABEL;
                     break;
               }
               break;
               
            case Token.KEYWORD1: // tag
               switch ( c ) {
                  case '>':
                     addToken(ip1-lastOffset, token);
                     lastOffset = ip1;
                     token = Token.NULL;
                     sk1 = false;
                     break;
                     
                  case ' ':
                  case '\t':
                     addToken(i-lastOffset, token);
                     lastOffset = i;
                     token = Token.KEYWORD2;
                     sk1 = false;
                     break;
                     
                  default:
                     if ( sk1 ) {
                        token = Token.KEYWORD2;
                        sk1 = false;
                     }
                     break;
               }
               break;

            case Token.KEYWORD2: // attribute
               switch ( c ) {
                   case '>':
                     addToken(ip1-lastOffset, token);
                     lastOffset = ip1;
                     token = Token.NULL;
                     break;
                    
                  case '/':
                     addToken(i-lastOffset, token);
                     lastOffset = i;
                     token = Token.KEYWORD1;
                     break;
                     
                  case '=':
                     addToken(i-lastOffset, token);
                     lastOffset = i;
                     token = Token.OPERATOR;
               }
               break;
               
            case Token.OPERATOR: // equal for attribute
               switch ( c ) {
                  case '\"':
                  case '\'':
                     addToken(i-lastOffset, token);
                     lastOffset = i;
                     if ( c == '\"' )
                        token = Token.LITERAL1;
                     else
                        token = Token.LITERAL2;
                     break;
               }
               break;
               
            case Token.LITERAL1:
            case Token.LITERAL2: // strings
               if ( ( token == Token.LITERAL1 && c == '\"' )
                 || ( token == Token.LITERAL2 && c == '\'' ) ) {
                  addToken(ip1-lastOffset, token);
                  lastOffset = ip1;
                  token = Token.KEYWORD1;
               }
               break;
            
            case Token.LABEL: // entity
               if ( c == ';' ) {
                  addToken(ip1-lastOffset, token);
                  lastOffset = ip1;
                  token = Token.NULL;
                  break;
               }
               break;
               
            case Token.COMMENT1: // Inside a comment
               if ( SyntaxUtilities.regionMatches(false, line, i, "-->") ) {
                  addToken((i+3)-lastOffset, token);
                  lastOffset = i+3;
                  token = Token.NULL;
               }
               break;

            case Token.COMMENT2: // Inside a declaration
               if ( SyntaxUtilities.regionMatches(false, line, i, ">") ) {
                  addToken(ip1-lastOffset, token);
                  lastOffset = ip1;
                  token = Token.NULL;
               }
               break;

            case Token.KEYWORD3: // Inside a processor instruction
               if ( SyntaxUtilities.regionMatches(false, line, i, "?>") ) {
                  addToken((i+2)-lastOffset, token);
                  lastOffset = i+2;
                  token = Token.NULL;
               }
               break;
               
            default:
               throw new InternalError("Invalid state: " + token);
         }
      }
      
      switch ( token ) {
         case Token.LABEL:
            addToken(length-lastOffset, Token.INVALID);
            token = Token.NULL;
            break;
            
         default:
            addToken(length-lastOffset, token);
            break;
      }
      
      return token;
   }
}


